#include "file_output_strategy.h"

#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <cstring>
#include <ctime>
#include <filesystem>
#include <syslog.h>
#include <system_error>
#include "time_util.h"
#include "async_fsync.h"

namespace Base {
namespace SystemLog {
FileOutputStrategy::FileOutputStrategy(const FileOutputOptions &options)
    : fileOptions_(options), lastRollTime_(std::chrono::system_clock::now())
{
    if (fileOptions_.filePathPattern.empty()) {
        throw std::invalid_argument("File path pattern cannot be empty");
    }

    // 确保目录存在
    auto dir = std::filesystem::path(fileOptions_.filePathPattern).parent_path();
    if (!dir.empty() && !std::filesystem::exists(dir)) {
        std::filesystem::create_directories(dir);
    }

    // 初始化AsyncFsync
    if (fileOptions_.useAsyncFsync) {
        asyncFsync_ = &AsyncFsync::GetInstance();
        asyncFsync_->SetSyncInterval(fileOptions_.asyncFsyncIntervalMs);
    }

    OpenFile();
}

FileOutputStrategy::~FileOutputStrategy()
{
    Close();
}

ErrCode FileOutputStrategy::Write(const char* data, size_t len)
{
    std::lock_guard<std::mutex> lock(mutex_);
    
    if (fileDescriptor_ < 0) {
        return ERR_FILE_NOT_OPEN;
    }

    auto err = RollFileIfNeeded(len);
    if (err != ERR_OK) {
        return err;
    }

    err = WriteToFile(data, len);
    if (err == ERR_OK) {
        currentSize_ += len;
    }
    return err;
}

ErrCode FileOutputStrategy::Flush()
{
    std::lock_guard<std::mutex> lock(mutex_);
    return FlushFile();
}

ErrCode FileOutputStrategy::Close()
{
    std::lock_guard<std::mutex> lock(mutex_);
    return CloseCurrentFile();
}

ErrCode FileOutputStrategy::OpenFile()
{
    auto fileName = GenerateFileName();
    fileDescriptor_ = open(fileName.c_str(), O_RDWR | O_CREAT | O_APPEND, 0644);

    if (fileDescriptor_ < 0) {
        syslog(LOG_ERR, "failed to open log file: %s, error: %s", fileName.c_str(), strerror(errno));
        return ERR_FILE_OPEN_FAILED;
    }

    // 获取当前文件大小
    struct stat st;
    if (fstat(fileDescriptor_, &st) == 0) {
        currentSize_ = static_cast<size_t>(st.st_size);
    } else {
        currentSize_ = 0;
    }

    // 将文件描述符添加到AsyncFsync管理
    if (asyncFsync_) {
        asyncFsync_->AddFile(fileDescriptor_);
    }

    lastRollTime_ = std::chrono::system_clock::now();
    return ERR_OK;
}

ErrCode FileOutputStrategy::CloseCurrentFile()
{
    if (fileDescriptor_ >= 0) {
        // 只在设置了异步同步且指针有效时才移除
        // 注意：在程序退出时，AsyncFsync可能已经被销毁，所以这里不调用
        // if (asyncFsync_) {
        //     asyncFsync_->RemoveFile(fileDescriptor_);
        // }
        
        FlushFile();
        close(fileDescriptor_);
        fileDescriptor_ = -1;
        currentSize_ = 0;
    }
    return ERR_OK;
}

ErrCode FileOutputStrategy::WriteToFile(const char* data, size_t len)
{
    ssize_t written = write(fileDescriptor_, data, len);
    return written == static_cast<ssize_t>(len) ? ERR_OK : ERR_IO_WRITE_FAILED;
}

ErrCode FileOutputStrategy::FlushFile()
{
    if (fileDescriptor_ >= 0) {
        if (fileOptions_.useAsyncFsync && asyncFsync_) {
            // 使用异步fsync，不阻塞当前线程
            // 注意：这里不直接fsync，而是让AsyncFsync在后台处理
            return ERR_OK;
        } else {
            // 同步fsync
            fsync(fileDescriptor_);
        }
    }
    return ERR_OK;
}

ErrCode FileOutputStrategy::ForceSync()
{
    std::lock_guard<std::mutex> lock(mutex_);
    if (fileDescriptor_ >= 0) {
        if (asyncFsync_) {
            // 请求立即同步
            asyncFsync_->RequestImmediateSync(fileDescriptor_);
        } else {
            // 直接同步
            fsync(fileDescriptor_);
        }
    }
    return ERR_OK;
}

ErrCode FileOutputStrategy::RollFileIfNeeded(size_t newDataSize)
{
    auto now = std::chrono::system_clock::now();
    bool sizeExceeded = (currentSize_ + newDataSize) >= fileOptions_.maxFileSize;
    bool timeExceeded = (now - lastRollTime_) >= fileOptions_.rollInterval;

    if (!sizeExceeded && !timeExceeded) {
        return ERR_OK;
    }

    auto err = CloseCurrentFile();
    if (err != ERR_OK) {
        return err;
    }

    return OpenFile();
}

std::string FileOutputStrategy::GenerateFileName() const
{
    auto now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
    return Utils::TimeUtil::FormatTime(now, fileOptions_.filePathPattern.c_str());
}

std::string FileOutputStrategy::GetCurrentFilePath() const
{
    return GenerateFileName();
}
} // namespace SystemLog
} // namespace Base
